#ifndef JSON_NODE_H
#define JSON_NODE_H

#include <vector>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <stdexcept>

#include "./JParserNamespace.h"

MY_JSON_PARSER_NAMESPACE_BEGIN
class JNode;

enum JNodeType {
	JSON_NULL,
	JSON_BOOL,
	JSON_INT,
	JSON_DOUBLE,
	JSON_STRING,
	JSON_ARRAY,
	JSON_OBJECT
};

union JNodeValue {
public:
	bool json_bool;
	std::int64_t json_int;
	double json_double;
	std::string* json_string;
	std::vector<myjson::JNode>* json_array;
	std::unordered_map<std::string, myjson::JNode>* json_object;
public:
	JNodeValue() :json_double(0.0l) {};
	~JNodeValue() = default;
};

struct TagBool {};

struct TagInt {};

struct TagDouble {};

class JNode
{
private:
	myjson::JNodeType node_type;
	myjson::JNodeValue node_value;
public:
	JNode();
	JNode(myjson::JNode&& other) noexcept;
	JNode(const myjson::JNode& other);
public:
	JNode(const bool& value, const myjson::TagBool& tag);
	JNode(const std::int64_t& value, const myjson::TagInt& tag);
	JNode(const double& value, const myjson::TagDouble& tag);
	JNode(const std::string& value);
	JNode(const char* value);
	JNode(const std::vector<myjson::JNode>& value);
	JNode(const std::unordered_map<std::string, myjson::JNode>& value);
	JNode(myjson::JNodeType type);
public:
	~JNode();
public:
	myjson::JNode& operator=(myjson::JNode&& other) noexcept;
	myjson::JNode& operator=(const myjson::JNode& other);
public:
	explicit operator bool() const;
	explicit operator std::int64_t() const;
	explicit operator double() const;
	explicit operator std::string* () const;
	explicit operator std::string() const;
	explicit operator std::vector<myjson::JNode>* () const;
	explicit operator std::vector<myjson::JNode>() const;
	explicit operator std::unordered_map<std::string, myjson::JNode>* () const;
	explicit operator std::unordered_map<std::string, myjson::JNode>() const;
private:
	void cleanup();
private:
	void createNull();
	void createBool(const bool& value);
	void createInt(const std::int64_t& value);
	void createDouble(const double& value);
	void createString(const std::string& value);
	void createArray(const std::vector<myjson::JNode>& value);
	void createObject(const std::unordered_map<std::string, myjson::JNode>& value);
	void createJNodeWithType(myjson::JNodeType type);
public:
	void updateObjectKeyValue(const std::string& key, const myjson::JNode& value);
	void updateObjectKeyValue(const std::string& key, myjson::JNode&& value);
	void deleteObjectKey(const std::string& key);
public:
	void addArrayValue(const myjson::JNode& value);
	void addArrayValue(myjson::JNode&& value);
	void popArrayValue();
public:
	std::string toJSONText();
	std::string toFormatJSONText(const std::uint64_t& indent = 1);
};
MY_JSON_PARSER_NAMESPACE_END

#endif // !JSON_NODE_H
